package com.zc.common.validator;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.xml.bind.ValidationException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * 字段校验辅助类
 */
@Slf4j
public class ValidationHelper {


    private static List<String> EXCLUDES_FIELDS = Arrays.asList("serialVersionUID");
    private final static String ENUM_KEY = "key";
    private static String PARAM_DATA_ERROR = "100001";


    public static ValidResult valid(Object object) throws BusinessValidException {

        ValidResult validResult = new ValidResult();

        // 获取object的类型
        Class<? extends Object> clazz = object.getClass();
        Field[] fields = getFields(clazz);
        // 获取该类型声明的成员
        // 遍历属性
        for (Field field : fields) {
            try {
                if (field == null || EXCLUDES_FIELDS.contains(field.getName())) {
                    continue;
                }
                // 对于private私有化的成员变量，通过setAccessible来修改器访问权限
                field.setAccessible(true);
                validate(field, object, validResult);
                // 重新设置会私有权限
                field.setAccessible(false);
            } catch (BusinessValidException e) {
                log.warn(" Validation Service Error ==>  field : {} , error : {} ",
                        clazz.getName() + "." + field.getName(),
                        e.getMessage());
                throw e;
            }
        }
        return validResult;
    }

    private static Field[] getFields(Class<? extends Object> clazz) {
        // 如果是Request，获取Request的父类
        Class<? extends Object> superClazz = clazz.getSuperclass();
        Field[] fileds = null;
        Field[] thisFields = clazz.getDeclaredFields();
        if (!superClazz.getName().contains("java.lang.Object")) {
            Field[] supperFields = clazz.getSuperclass().getDeclaredFields();
            fileds = new Field[thisFields.length + supperFields.length];
            System.arraycopy(thisFields, 0, fileds, 0, thisFields.length);
            System.arraycopy(supperFields, 0, fileds, thisFields.length, supperFields.length);
            for (int i = 0; i < thisFields.length; i++) {
                fileds[i] = thisFields[i];
            }
            for (int i = thisFields.length; i < supperFields.length; i++) {
                fileds[i] = supperFields[i];
            }
        } else {
            fileds = thisFields;
        }
        return fileds;
    }

    /**
     * 校验处理
     *
     * @param field
     * @param object
     * @throws ValidationException
     * @author tany@shishike.com
     * @2015年2月27日
     */

    private static void validate(Field field, Object object, ValidResult validResult) throws BusinessValidException {
        Object value = null;
        try {
            value = field.get(object);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            log.error(e.getMessage(), e);
            throw new BusinessValidException("校验服务异常");
        }

        // 获取注解
        ValidField validation = field.getAnnotation(ValidField.class);
        if (validation == null) {
            return;
        }
        String fieldName = field.getName();

        // 描述
        String description = StringUtils.isBlank(validation.msg()) ? fieldName : validation.msg();
        // 校验正则表达式
        String regexExpression = validation.regexExpression();
        // 是否为空
        if (!validation.isNull()) {
            if (value == null || StringUtils.isBlank(value.toString())) {
                validResult.addError(fieldName, description + "为空");
                return;
            }
        }
        if (value != null && StringUtils.isNotBlank(value.toString())) {
            int len = validation.fixedLength();// 定长
            int max = validation.maxLength();// 获取最大值
            int min = validation.minLength();// 获取最小值
            int length = value.toString().length(); // 字段长度
            if (len != 0) {
                if (length != len) {
                    validResult.addError(fieldName, description + "长度为" + len + "位长度定值 ");
                }
            } else {
                if (max != 0 && length > max) {
                    validResult.addError(fieldName, description + "长度不能大于" + max);
                }

                if (min != 0 && length < min) {
                    validResult.addError(fieldName, description + "长度不能小于" + min);
                }
            }
            String msg = description;

            if (validation.regexType() != RegexType.NONE) {
                switch (validation.regexType()) {
                    case NONE:
                        break;
                    case BOOL:
                        if (!(value.toString().equals("true") || value.toString().equals("false"))) {
                            validResult.addError(fieldName, description + "必须为0或者1");
                        }
                    case CHARACTER:// 字符
                        if (StringUtils.isNotBlank(regexExpression)
                                && !RegexUtils.validByRegex(value.toString(), regexExpression)) {
                            validResult.addError(fieldName, description + "格式错误，校验规则 "
                                    + regexExpression);
                        }
                        break;
                    case INT: // 整数
                        if (!RegexUtils.isInteger(value.toString(), regexExpression)) {
                            validResult.addError(fieldName, description + "请输入正确格式整型数字");
                        }
                        break;

                    case LONG: // 长整型
                        if (!RegexUtils.isLong(value.toString(), regexExpression)) {
                            validResult.addError(fieldName, description + "请输入正确格式长整型数字");
                        }
                        break;
                    case DECIMAL: // 浮点型
                        if (!RegexUtils.isDecimal(value.toString(), regexExpression)) {
                            validResult.addError(fieldName, description + "请输入正确格式的浮点型数字");
                        }
                        break;
                    case EMAIL: // 电子邮件
                        if (!RegexUtils.isEmail(value.toString())) {
                            validResult.addError(fieldName, description + "格式错误(应为Email格式)");
                        }
                        break;
                    case IP: // ip地址
                        if (!RegexUtils.isIP(value.toString())) {
                            validResult.addError(fieldName, description + "格式错误(应为ip地址格式)");
                        }
                        break;
                    case URL: // 网站地址
                        if (!RegexUtils.isURL(value.toString())) {
                            validResult.addError(fieldName, description + "格式错误(应为网站地址格式)");
                        }
                        break;
                    case PHONENUMBER: // 电话号码
                        if (!RegexUtils.isPhoneNumber(value.toString(), regexExpression)) {
                            validResult.addError(fieldName, description + "格式错误(应为电话、手机号码格式)");
                        }
                        break;
                    case DATE: // 日期
                        if (StringUtils.isBlank(regexExpression)) {
                            regexExpression = RegexUtils.YYYY_MM_DD_HH_MM_SS;
                        }
                        if (!RegexUtils.isDate(value.toString(), regexExpression)) {
                            validResult.addError(fieldName, description + "格式错误");
                        }
                        break;
                    case ARRAY:
                        checkArray(validResult, (List) value, validation, fieldName, description);
                        break;
                    case MAP:
                        break;
                    case OBJECT:
                        try {
                            valid(value);
                        } catch (BusinessValidException e) {
                            // logger.error(e.getMessage(), e);
                            throw e;
                        }
                        break;
                    case ENUM:// 枚举类型校验
                        //  validateEnum(value.toString(), description, validation.clazz(), validation.excludes());
                    default:
                        break;
                }
            }
        }
    }

    private static void checkArray(ValidResult validResult, List value, ValidField validation, String fieldName, String description) {
        try {
            Integer minSize = validation.minSize();
            Integer maxSize = validation.maxSize();
            List list = value;
            if (list == null) {
                validResult.addError(fieldName, description + "为空");
                return;
            }
            if (list.size() < minSize) {
                validResult.addError(fieldName, description + "长度必须大于" + minSize);
            }
            if (list.size() > maxSize) {
                validResult.addError(fieldName, description + "长度必须小于" + maxSize);
            }
            if (list.size() > 0) {
                for (int i = 0; i < list.size(); i++) {
                    valid(list.get(i));
                }
            }
        } catch (BusinessValidException e) {
            throw e;
        }
    }

    /**
     * 验证枚举类型
     *
     * @param clazz
     * @return
     */
    public static void validateEnum(String filedValue, String description, Class clazz, String[] excludes) throws BusinessValidException {
        if (!clazz.isEnum()) {// clazz 是否为枚举类型
            throw new BusinessValidException(PARAM_DATA_ERROR, description + "请输入正确的枚举类型");
        }

        List<String> enumKeyValues = new ArrayList<>();
        try {
            Object[] objects = clazz.getEnumConstants();
            for (Object object : objects) {
                // 获取枚举类型的values方法
                Method m = object.getClass().getDeclaredMethod("values", null);
                // 执行values方法获取返回的枚举类型
                Object[] result = (Object[]) m.invoke(object, null);
                List list = Arrays.asList(result);
                Iterator it = list.iterator();
                while (it.hasNext()) {
                    Object objOne = it.next();
                    Field key = objOne.getClass().getDeclaredField(ENUM_KEY);
                    key.setAccessible(true);
                    enumKeyValues.add(key.get(objOne).toString());
                    key.setAccessible(false);
                }
                break;
            }

        } catch (Exception e) {
            log.error("调用验证服务校验枚举类型异常", e);
            throw new BusinessValidException(PARAM_DATA_ERROR, description + "校验调用验证服务验证枚举类型异常");
        }
        if (excludes.length != 0) {
            boolean containsExclude = false;
            List<String> validValues = new ArrayList<>();
            validValues.addAll(enumKeyValues);
            for (String excute : excludes) {
                if (filedValue.equals(excute)) {
                    containsExclude = true;
                }
                validValues.remove(excute);
            }
            if (containsExclude) {
                throw new BusinessValidException(PARAM_DATA_ERROR, description + "校验失败，请输入" + validValues.toString() + "其中一位");
            }
        }

        if (!enumKeyValues.contains(filedValue)) {
            throw new BusinessValidException(PARAM_DATA_ERROR, description + "校验失败，请输入" + enumKeyValues.toString() + "其中一位");
        }
    }
}
